﻿using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Folding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    public class CustomFoldingManager : FoldingManager
    {
        public CustomFoldingManager(TextDocument document) : base(document)
        {

        }

        #region Install
        /// <summary>
        /// Adds Folding support to the specified text area.
        /// Warning: The folding manager is only valid for the text area's current document. The folding manager
        /// must be uninstalled before the text area is bound to a different document.
        /// </summary>
        /// <returns>The <see cref="FoldingManager"/> that manages the list of foldings inside the text area.</returns>
        public new static CustomFoldingManager Install(TextArea textArea)
        {
            if (textArea == null)
                throw new ArgumentNullException("textArea");
            return new CustomFoldingManagerInstallation(textArea) as CustomFoldingManager;
        }
        #endregion

        /// <summary>
        /// Uninstalls the folding manager.
        /// </summary>
        /// <exception cref="ArgumentException">The specified manager was not created using <see cref="Install"/>.</exception>
        public new static void Uninstall(FoldingManager manager)
        {
            if (manager == null)
                throw new ArgumentNullException("manager");
            CustomFoldingManagerInstallation installation = manager as CustomFoldingManagerInstallation;
            if (installation != null)
            {
                installation.Uninstall();
            }
            else
            {
                throw new ArgumentException("FoldingManager was not created using FoldingManager.Install");
            }
        }

        /// <summary>
        /// 只应该在打开文件时临时将此属性值设置为 true。
        /// 用以在UpdateFoldings()时自动折叠某些被指定为应该自动折叠的块。
        /// </summary>
        public bool FoldSpecialFoldings { get; set; } = false;

        /// <summary>
        /// 只应该在打开文件时临时将此属性值设置为 true——否则会导致折叠块无法展开。
        /// 用以在UpdateFoldings()时自动折叠某些被指定为应该自动折叠的块。
        /// </summary>
        public bool FoldMathFoldings { get; set; } = false;

        public new void UpdateFoldings(IEnumerable<NewFolding> newFoldings, int firstErrorOffset)
        {
            base.UpdateFoldings(newFoldings, firstErrorOffset);

            //折叠所有单行的图像链接文本、二维文字表、自定义折叠区等。
            //这个只应该折叠一次，否则可能造成无法顺利展开折叠块并编辑——因为会总是被自动折叠起来。
            if (FoldSpecialFoldings)
            {
                foreach (var i in this.AllFoldings)
                {
                    var folding = i.Tag as ICSharpCode.AvalonEdit.Folding.NewFolding;
                    if (folding != null && folding.IsSpecial)
                    {
                        i.IsFolded = true;
                    }
                }
            }

            if (FoldMathFoldings)
            {
                foreach (var i in this.AllFoldings)
                {
                    var folding = i.Tag as ICSharpCode.AvalonEdit.Folding.NewFolding;
                    if (folding != null && (folding.Name.StartsWith("<math", StringComparison.CurrentCultureIgnoreCase)||
                        folding.Name.StartsWith("<style", StringComparison.CurrentCultureIgnoreCase)||
                        folding.Name.StartsWith("<script", StringComparison.CurrentCultureIgnoreCase)))
                    {
                        i.IsFolded = true;
                    }
                }
            }
        }
    }

    sealed class CustomFoldingManagerInstallation : CustomFoldingManager
    {
        TextArea textArea;
        FoldingMargin margin;
        FoldingElementGenerator generator;

        public CustomFoldingManagerInstallation(TextArea textArea) : base(textArea.Document)
        {
            this.textArea = textArea;
            margin = new FoldingMargin() { FoldingManager = this };
            generator = new FoldingElementGenerator() { FoldingManager = this };
            textArea.LeftMargins.Add(margin);
            textArea.TextView.Services.AddService(typeof(FoldingManager), this);
            // HACK: folding only works correctly when it has highest priority
            textArea.TextView.ElementGenerators.Insert(0, generator);
            textArea.Caret.PositionChanged += textArea_Caret_PositionChanged;
        }

        public void Uninstall()
        {
            Clear();
            if (textArea != null)
            {
                textArea.Caret.PositionChanged -= textArea_Caret_PositionChanged;
                textArea.LeftMargins.Remove(margin);
                textArea.TextView.ElementGenerators.Remove(generator);
                textArea.TextView.Services.RemoveService(typeof(FoldingManager));
                margin = null;
                generator = null;
                textArea = null;
            }
        }

        void textArea_Caret_PositionChanged(object sender, EventArgs e)
        {
            // Expand Foldings when Caret is moved into them.
            int caretOffset = textArea.Caret.Offset;
            foreach (FoldingSection s in GetFoldingsContaining(caretOffset))
            {
                if (s.IsFolded && s.StartOffset < caretOffset && caretOffset < s.EndOffset)
                {
                    s.IsFolded = false;
                }
            }
        }
    }
}